﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using Library.Models;
using Library.TwiCommunication;
using Library.Utils;
using log4net;

namespace Library.Repository
{
    public class DeviceRepository : IDeviceRepository
    {
        private static readonly ILog Log = LogManager.GetLogger("DeviceRepository");
        private readonly TwiDeviceList _devices;
        private readonly String _xmlPath;



        public DeviceRepository()
        {
            Log.Info("constructor");
            
            if (UserRepository.IsLinux)
                _xmlPath = @"/home/pi/InteligentnyDom/SerialisationToFileTest/bin/Debug/devices.xml";
            else
                _xmlPath =
                    @"D:\raspberryPI\Projects\trunk\InteligentnyDom\SerialisationToFileTest\bin\Debug\devices.xml";

            var serializer = new XmlSerializer(typeof (TwiDeviceList));
            TextReader rstream = new StreamReader(_xmlPath);

            _devices = (TwiDeviceList) serializer.Deserialize(rstream);
            rstream.Close();

            Log.Info("constructor_end");
        }

        #region IDeviceRepository Members

        public TwiDevice Add(TwiDevice device)
        {
            Log.Info("Add: " + device.Address + " " + device.Description);

            if (_devices.List.Count(d => d.Address == device.Address) != 0)
                throw new DeviceAlreadyExistsException("Użądzenie o adresie: " + device.Address +
                                                       " już istnieje w systemie");

            _devices.List.Add(device);
            Persist();

            Log.Info("Add_end");
            return device;
        }

        public TwiDevice GetOrDie(int address)
        {
            Log.Info("GetOrDie: " + address);
            
            try
            {
                return _devices.List.Single(d => d.Address == address);
            }
            catch (Exception)
            {
                string list = "";
                foreach (TwiDevice device in _devices.List)
                {
                    list += device.Address + " ";
                }
                throw new DeviceNotFoundException("Użądzenie o adresie: " + address +
                                                  " nie istnieje w systemie. Dostępne: " + list);
            }
        }

        public List<TwiDevice> GetAll()
        {
            Log.Info("GetAll");

            return _devices.List;
        }

        public List<TwiDevice> GetAllWithData()
        {
            Log.Info("GetAllWithData_start");
            var communicator = ServiceLocator.Instance.GetService<ITwiCommunicator>();

            var list = _devices.List;

            foreach(var device in list)
            {
                communicator.ReadDevice(device);
                
            }
            Log.Info("GetAllWithData_end");
            return _devices.List;
        }

        public void WriteDevicePin(int address, int pin, bool value)
        {
            ITwiCommunicator communicator = ServiceLocator.Instance.GetService<ITwiCommunicator>();

            try
            {
                var device = GetOrDie(address);
                if(device is OutDevice || device is InOutDevice)
                {
                    device = communicator.ReadDevice(device);
                    if(device is OutDevice)
                    {
                        Log.Info("Przed: " + device.ToString());
                        (device as OutDevice).Pins.Find(p => p.Pin == pin).Value = value;
                        Log.Info("Ustawiam pin "+ pin +" na " + value);
                        Log.Info("Po:    " + device.ToString());
                    }
                    if (device is InOutDevice)
                    {
                        (device as InOutDevice).Pins.Find(p => p.Pin == pin).Value = value;
                        Log.Info("Ustawiam pin " + pin + " na " + value);

                    }
                    communicator.WriteDevice(device);
                }
            }
            catch (Exception)
            {
            }

        }

        public void Remove(int address)
        {
            Log.Info("Remove: " + address);

            TwiDevice device = GetOrDie(address);
            _devices.List.Remove(device);
            Persist();

            Log.Info("Remove_end");
        }

        #endregion

        private void Persist()
        {
            Log.Info("Persist");

            var serializer = new XmlSerializer(typeof (TwiDeviceList));
            TextWriter wstream = new StreamWriter(_xmlPath);
            serializer.Serialize(wstream, _devices);
            wstream.Close();
            
            Log.Info("Persist_end");
        }

        public static void GenerateTestDevices()
        {
            var devices = new TwiDeviceList();
            var serializer = new XmlSerializer(typeof (TwiDeviceList));
            TextWriter wstream =
                new StreamWriter(
                    @"D:\raspberryPI\Projects\trunk\InteligentnyDom\SerialisationToFileTest\bin\Debug\devices.xml");

            var od = new OutDevice {Address = 11, Description = "Gniazdka kuchnia pcf"};
            var td = new TermDevice {Address = 22, Description = "Temperatura salon LM75", Temp = 125};
            var id = new InDevice {Address = 33, Description = "Okna"};

            od.Pins.Add(new DevicePin {Pin = 1, Value = true, Name = "gniazdko lodówka"});
            od.Pins.Add(new DevicePin {Pin = 3, Value = false, Name = "gniazdko ekspres"});

            id.Pins.Add(new DevicePin {Pin = 2, Value = true, Name = "okno tarasowe"});
            id.Pins.Add(new DevicePin {Pin = 5, Value = true, Name = "okno wykusz"});

            devices.List.Add(od);
            devices.List.Add(id);
            devices.List.Add(td);

            serializer.Serialize(wstream, devices);
            wstream.Close();
        }
    }


    public interface IDeviceRepository
    {
        TwiDevice Add(TwiDevice device);

        TwiDevice GetOrDie(int address);

        List<TwiDevice> GetAll();

        List<TwiDevice> GetAllWithData();

        void WriteDevicePin(int address, int pin, bool value);

        void Remove(int address);
    }
}